package com.jie.component.lock.aspect;


import cn.hutool.core.util.StrUtil;
import com.jie.common.util.LogUtil;
import com.jie.component.exception.base.FrameException;
import com.jie.component.exception.enums.FrameError;
import com.jie.component.lock.annotion.ServiceLock;
import com.jie.component.lock.core.LockType;
import com.jie.component.lock.factory.ServiceLockFactory;
import com.jie.component.lock.locker.ServiceLocker;
import com.jie.component.lock.lockinfo.LockInfoHandle;
import com.jie.component.lock.lockinfo.LockInfoType;
import com.jie.component.lock.lockinfo.factory.LockInfoHandleFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @author jie
 * @version 1.0 2025-02-10 分布式锁切面
 */
@AllArgsConstructor
@Slf4j
@Order(-10) // 让锁的切面在事务的切面之前执行
@Aspect
public class ServiceLockAspect {

    private final LockInfoHandleFactory lockInfoHandleFactory;

    private final ServiceLockFactory serviceLockFactory;

    @Around("@annotation(serviceLock)")
    public Object around(ProceedingJoinPoint joinPoint, ServiceLock serviceLock) throws Throwable {
        LockInfoHandle lockInfoHandle = lockInfoHandleFactory.getLockInfoHandle(LockInfoType.SERVICE_LOCK);
        String lockName = lockInfoHandle.getLockName(joinPoint, serviceLock.name(), serviceLock.keys());
        LockType lockType = serviceLock.lockType();
        long waitTime = serviceLock.waitTime();
        TimeUnit timeUnit = serviceLock.timeUnit();

        ServiceLocker lock = serviceLockFactory.getLock(lockType);
        boolean result = lock.tryLock(lockName, timeUnit, waitTime);
        if (result) {
            try {
                return joinPoint.proceed();
            } finally {
                lock.unlock(lockName);
            }
        } else {
            LogUtil.acquireLockTimeOutLog(lockName);
            String customLockTimeoutStrategy = serviceLock.customLockTimeoutStrategy();
            if (StrUtil.isNotBlank(customLockTimeoutStrategy)) {
                // 执行自定义的超时策略
                return handleCustomLockTimeoutStrategy(customLockTimeoutStrategy, joinPoint);
            } else {
                // 执行默认的超时策略
                serviceLock.lockTimeoutStrategy().handler(lockName);
            }
            return null;
        }
    }

    /**
     * 自定义的锁超时策略
     * @param customLockTimeoutStrategy
     * @param joinPoint
     * @return
     */
    public Object handleCustomLockTimeoutStrategy(String customLockTimeoutStrategy, JoinPoint joinPoint) {
        Method currentMethod = ((MethodSignature) joinPoint.getSignature()).getMethod();
        Object target = joinPoint.getTarget();
        Method handleMethod = null;
        try {
            handleMethod = target.getClass().getDeclaredMethod(customLockTimeoutStrategy, currentMethod.getParameterTypes());
            handleMethod.setAccessible(true);
        } catch (NoSuchMethodException e) {
//            throw new RuntimeException("Illegal annotation param customLockTimeoutStrategy :" + customLockTimeoutStrategy,e);
            throw new FrameException(FrameError.ILLEGAL_ANNOTATION_PARAM_LOCK_TIME_OUT_STRATEGY);
        }
        // 调用函数
        Object[] args = joinPoint.getArgs();
        Object result;
        try {
            result = handleMethod.invoke(target, args);
        } catch (IllegalAccessException e) {
//            throw new RuntimeException("Fail to illegal access custom lock timeout handler: " + customLockTimeoutStrategy ,e);
            throw new FrameException(FrameError.ILLEGAL_ACCESS_CUSTOM_LOCK_TIME_OUT_HANDLER);
        } catch (InvocationTargetException e) {
//            throw new RuntimeException("Fail to invoke custom lock timeout handler: " + customLockTimeoutStrategy ,e);
            throw new FrameException(FrameError.FAIL_INVOKE_CUSTOM_LOCK_TIMEOUT_HANDLER);
        }
        return result;
    }
}
